package com.demo.statemachine.d20;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.StateDoActionPolicy;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.statemachine.config.configurers.StateConfigurer;
import org.springframework.statemachine.region.RegionExecutionPolicy;
import org.springframework.statemachine.transition.TransitionConflictPolicy;

import java.util.concurrent.TimeUnit;

@Configuration("testStateMachineBuilder")
@EnableStateMachine
public class TestStateMachineBuilder {
    @Autowired
    private BeanFactory beanFactory;
    private final static String MACHINEID = "TestMachine";

    public StateMachine<TestStates, TestEvents> build() throws Exception {
        StateMachineBuilder.Builder<TestStates, TestEvents> builder = StateMachineBuilder.builder();
        builder.configureConfiguration().withConfiguration()
                .machineId(MACHINEID)
                .beanFactory(beanFactory)
                .listener(new TestListener())
                .stateDoActionPolicy(StateDoActionPolicy.TIMEOUT_CANCEL)
                .stateDoActionPolicyTimeout(1, TimeUnit.SECONDS)
                .regionExecutionPolicy(RegionExecutionPolicy.PARALLEL)
                .transitionConflictPolicy(TransitionConflictPolicy.CHILD)
                ;
//        builder.configureModel()
//                ;
        builder.configureStates().withStates()
                .initial(TestStates.IDLE)
                .state(TestStates.BUSY)
                .state(TestStates.SUSPEND)


                .and().withStates()
                    .parent(TestStates.BUSY)
                    .initial(TestStates.BUSY_START)
                    .state(TestStates.BUSY_END)
                    .state(TestStates.FAULT)

                    .fork(TestStates.T1_FORK)
                    .state(TestStates.T1_TASK)
                    .join(TestStates.T1_JOIN)
                    .choice(TestStates.T1_CHOICE)

                    .fork(TestStates.T2_FORK)
                    .state(TestStates.T2_TASK)
                    .join(TestStates.T2_JOIN)
                    .choice(TestStates.T2_CHOICE)

                    .fork(TestStates.T3_FORK)
                    .state(TestStates.T3_TASK)
                    .join(TestStates.T3_JOIN)
                    .choice(TestStates.T3_CHOICE)

                    .fork(TestStates.T4_FORK)
                    .state(TestStates.T4_TASK)
                    .join(TestStates.T4_JOIN)
                    .choice(TestStates.T4_CHOICE)

                    .fork(TestStates.T5_FORK)
                    .state(TestStates.T5_TASK)
                    .join(TestStates.T5_JOIN)
                    .choice(TestStates.T5_CHOICE)

                    .fork(TestStates.T6_FORK)
                    .state(TestStates.T6_TASK)
                    .join(TestStates.T6_JOIN)
                    .choice(TestStates.T6_CHOICE)


                    .fork(TestStates.T7_FORK)
                    .state(TestStates.T7_TASK)
                    .join(TestStates.T7_JOIN)
                    .choice(TestStates.T7_CHOICE)


                    .fork(TestStates.T8_FORK)
                    .state(TestStates.T8_TASK)
                    .join(TestStates.T8_JOIN)
                    .choice(TestStates.T8_CHOICE)
                    .history(TestStates.HISTORY, StateConfigurer.History.DEEP)


                    .and().withStates()
                        .parent(TestStates.T1_TASK)
                        .initial(TestStates.T1_TASK1_START)
                        .state(TestStates.T1_TASK1_STEP1)
                        .state(TestStates.T1_TASK1_STEP2)
                        .state(TestStates.T1_TASK1_STEP3)
                        .end(TestStates.T1_TASK1_END)
                    .and().withStates()
                        .parent(TestStates.T2_TASK)
                        .initial(TestStates.T2_TASK1_START)
                        .state(TestStates.T2_TASK1_STEP1)
                        .state(TestStates.T2_TASK1_STEP2)
                        .state(TestStates.T2_TASK1_STEP3)
                        .end(TestStates.T2_TASK1_END)
                    .and().withStates()
                        .parent(TestStates.T3_TASK)
                        .initial(TestStates.T3_TASK1_START)
                        .state(TestStates.T3_TASK1_STEP1)
                        .state(TestStates.T3_TASK1_STEP2)
                        .state(TestStates.T3_TASK1_STEP3)
                        .end(TestStates.T3_TASK1_END)
                    .and().withStates()
                        .parent(TestStates.T4_TASK)
                        .initial(TestStates.T4_TASK1_START)
                        .state(TestStates.T4_TASK1_STEP1)
                        .state(TestStates.T4_TASK1_STEP2)
                        .state(TestStates.T4_TASK1_STEP3)
                        .end(TestStates.T4_TASK1_END)
                    .and().withStates()
                        .parent(TestStates.T5_TASK)
                        .initial(TestStates.T5_TASK1_START)
                        .state(TestStates.T5_TASK1_STEP1)
                        .state(TestStates.T5_TASK1_STEP2)
                        .state(TestStates.T5_TASK1_STEP3)
                        .end(TestStates.T5_TASK1_END)
                    .and().withStates()
                        .parent(TestStates.T6_TASK)
                        .initial(TestStates.T6_TASK1_START)
                        .state(TestStates.T6_TASK1_STEP1)
                        .state(TestStates.T6_TASK1_STEP2)
                        .state(TestStates.T6_TASK1_STEP3)
                        .end(TestStates.T6_TASK1_END)
                    .and().withStates()
                        .parent(TestStates.T7_TASK)
                        .initial(TestStates.T7_TASK1_START)
                        .state(TestStates.T7_TASK1_STEP1)
                        .state(TestStates.T7_TASK1_STEP2)
                        .state(TestStates.T7_TASK1_STEP3)
                        .end(TestStates.T7_TASK1_END)
                    .and().withStates()
                        .parent(TestStates.T8_TASK)
                        .initial(TestStates.T8_TASK1_START)
                        .state(TestStates.T8_TASK1_STEP1)
                        .state(TestStates.T8_TASK1_STEP2)
                        .state(TestStates.T8_TASK1_STEP3)
                        .end(TestStates.T8_TASK1_END)

        ;


        builder.configureTransitions()

                .withExternal()
                    .source(TestStates.IDLE)
                    .target(TestStates.BUSY)
                    .event(TestEvents.IDLE_BUSY)
                    .action(TestActions.IDLE_TRANSIT_ACTION,TestActions.ERROR_ACTION)
                .and().withExternal()
                    .source(TestStates.BUSY)
                    .target(TestStates.IDLE)
                    .event(TestEvents.BUSY_IDLE)
                .and().withExternal()
                    .source(TestStates.BUSY)
                    .target(TestStates.SUSPEND)
                    .event(TestEvents.BUSY_SUSPEND)
                .and().withExternal()
                    .source(TestStates.SUSPEND)
                    .target(TestStates.HISTORY)
                    .event(TestEvents.SUSPEND_HISTORY)
                .and().withExternal()
                    .source(TestStates.BUSY_START)
                    .target(TestStates.T1_FORK)
                    .event(TestEvents.BUSY_START_T1_FORK)

                .and().withFork()
                    .source(TestStates.T1_FORK)
                    .target(TestStates.T1_TASK)
                .and().withExternal()
                    .source(TestStates.T1_TASK1_START)
                    .target(TestStates.T1_TASK1_STEP1).event(TestEvents.T1_TASK1_START_STEP1)
                .and().withExternal()
                    .source(TestStates.T1_TASK1_STEP1)
                    .target(TestStates.T1_TASK1_STEP2).event(TestEvents.T1_TASK1_STEP1_STEP2)
                .and().withExternal()
                    .source(TestStates.T1_TASK1_STEP2)
                    .target(TestStates.T1_TASK1_STEP3).event(TestEvents.T1_TASK1_STEP2_STEP3)
                .and().withExternal()
                    .source(TestStates.T1_TASK1_STEP3)
                    .target(TestStates.T1_TASK1_END).event(TestEvents.T1_TASK1_STEP3_END)
                .and().withJoin()
                    .source(TestStates.T1_TASK)
                    .target(TestStates.T1_JOIN)
                .and().withExternal()
                    .source(TestStates.T1_JOIN)
                    .target(TestStates.T1_CHOICE).event(TestEvents.T1_JOIN_CHOICE)
                .and().withChoice()
                    .source(TestStates.T1_CHOICE)
                    .first(TestStates.FAULT,TestGuards.T1_CHOICE_GUARD)
                    .last(TestStates.T2_FORK)

                .and().withFork()
                    .source(TestStates.T2_FORK)
                    .target(TestStates.T2_TASK)
                .and().withExternal()
                    .source(TestStates.T2_TASK1_START)
                    .target(TestStates.T2_TASK1_STEP1).event(TestEvents.T2_TASK1_START_STEP1)
                .and().withExternal()
                    .source(TestStates.T2_TASK1_STEP1)
                    .target(TestStates.T2_TASK1_STEP2).event(TestEvents.T2_TASK1_STEP1_STEP2)
                .and().withExternal()
                    .source(TestStates.T2_TASK1_STEP2)
                    .target(TestStates.T2_TASK1_STEP3).event(TestEvents.T2_TASK1_STEP2_STEP3)
                .and().withExternal()
                    .source(TestStates.T2_TASK1_STEP3)
                    .target(TestStates.T2_TASK1_END).event(TestEvents.T2_TASK1_STEP3_END)
                .and().withJoin()
                    .source(TestStates.T2_TASK)
                    .target(TestStates.T2_JOIN)
                .and().withExternal()
                    .source(TestStates.T2_JOIN)
                    .target(TestStates.T2_CHOICE).event(TestEvents.T2_JOIN_CHOICE)
                .and().withChoice()
                    .source(TestStates.T2_CHOICE)
                    .first(TestStates.FAULT,TestGuards.T2_CHOICE_GUARD)
                    .last(TestStates.T3_FORK)


                .and().withFork()
                    .source(TestStates.T3_FORK)
                    .target(TestStates.T3_TASK)
                .and().withExternal()
                    .source(TestStates.T3_TASK1_START)
                    .target(TestStates.T3_TASK1_STEP1).event(TestEvents.T3_TASK1_START_STEP1)
                .and().withExternal()
                    .source(TestStates.T3_TASK1_STEP1)
                    .target(TestStates.T3_TASK1_STEP2).event(TestEvents.T3_TASK1_STEP1_STEP2)
                .and().withExternal()
                    .source(TestStates.T3_TASK1_STEP2)
                    .target(TestStates.T3_TASK1_STEP3).event(TestEvents.T3_TASK1_STEP2_STEP3)
                .and().withExternal()
                    .source(TestStates.T3_TASK1_STEP3)
                    .target(TestStates.T3_TASK1_END).event(TestEvents.T3_TASK1_STEP3_END)
                .and().withJoin()
                    .source(TestStates.T3_TASK)
                    .target(TestStates.T3_JOIN)
                .and().withExternal()
                    .source(TestStates.T3_JOIN)
                    .target(TestStates.T3_CHOICE).event(TestEvents.T3_JOIN_CHOICE)
                .and().withChoice()
                    .source(TestStates.T3_CHOICE)
                    .first(TestStates.FAULT,TestGuards.T3_CHOICE_GUARD)
                    .last(TestStates.T4_FORK)


                .and().withFork()
                    .source(TestStates.T4_FORK)
                    .target(TestStates.T4_TASK)
                .and().withExternal()
                    .source(TestStates.T4_TASK1_START)
                    .target(TestStates.T4_TASK1_STEP1).event(TestEvents.T4_TASK1_START_STEP1)
                .and().withExternal()
                    .source(TestStates.T4_TASK1_STEP1)
                    .target(TestStates.T4_TASK1_STEP2).event(TestEvents.T4_TASK1_STEP1_STEP2)
                .and().withExternal()
                    .source(TestStates.T4_TASK1_STEP2)
                    .target(TestStates.T4_TASK1_STEP3).event(TestEvents.T4_TASK1_STEP2_STEP3)
                .and().withExternal()
                    .source(TestStates.T4_TASK1_STEP3)
                    .target(TestStates.T4_TASK1_END).event(TestEvents.T4_TASK1_STEP3_END)
                .and().withJoin()
                    .source(TestStates.T4_TASK)
                    .target(TestStates.T4_JOIN)
                .and().withExternal()
                    .source(TestStates.T4_JOIN)
                    .target(TestStates.T4_CHOICE).event(TestEvents.T4_JOIN_CHOICE)
                .and().withChoice()
                    .source(TestStates.T4_CHOICE)
                    .first(TestStates.FAULT,TestGuards.T4_CHOICE_GUARD)
                    .last(TestStates.T5_FORK)


                .and().withFork()
                    .source(TestStates.T5_FORK)
                    .target(TestStates.T5_TASK)
                .and().withExternal()
                    .source(TestStates.T5_TASK1_START)
                    .target(TestStates.T5_TASK1_STEP1).event(TestEvents.T5_TASK1_START_STEP1)
                .and().withExternal()
                    .source(TestStates.T5_TASK1_STEP1)
                    .target(TestStates.T5_TASK1_STEP2).event(TestEvents.T5_TASK1_STEP1_STEP2)
                .and().withExternal()
                    .source(TestStates.T5_TASK1_STEP2)
                    .target(TestStates.T5_TASK1_STEP3).event(TestEvents.T5_TASK1_STEP2_STEP3)
                .and().withExternal()
                    .source(TestStates.T5_TASK1_STEP3)
                    .target(TestStates.T5_TASK1_END).event(TestEvents.T5_TASK1_STEP3_END)
                .and().withJoin()
                    .source(TestStates.T5_TASK)
                    .target(TestStates.T5_JOIN)
                .and().withExternal()
                    .source(TestStates.T5_JOIN)
                    .target(TestStates.T5_CHOICE).event(TestEvents.T5_JOIN_CHOICE)
                .and().withChoice()
                    .source(TestStates.T5_CHOICE)
                    .first(TestStates.FAULT,TestGuards.T5_CHOICE_GUARD)
                    .last(TestStates.T6_FORK)


                .and().withFork()
                    .source(TestStates.T6_FORK)
                    .target(TestStates.T6_TASK)
                .and().withExternal()
                    .source(TestStates.T6_TASK1_START)
                    .target(TestStates.T6_TASK1_STEP1).event(TestEvents.T6_TASK1_START_STEP1)
                .and().withExternal()
                    .source(TestStates.T6_TASK1_STEP1)
                    .target(TestStates.T6_TASK1_STEP2).event(TestEvents.T6_TASK1_STEP1_STEP2)
                .and().withExternal()
                    .source(TestStates.T6_TASK1_STEP2)
                    .target(TestStates.T6_TASK1_STEP3).event(TestEvents.T6_TASK1_STEP2_STEP3)
                .and().withExternal()
                    .source(TestStates.T6_TASK1_STEP3)
                    .target(TestStates.T6_TASK1_END).event(TestEvents.T6_TASK1_STEP3_END)
                .and().withJoin()
                    .source(TestStates.T6_TASK)
                    .target(TestStates.T6_JOIN)
                .and().withExternal()
                    .source(TestStates.T6_JOIN)
                    .target(TestStates.T6_CHOICE).event(TestEvents.T6_JOIN_CHOICE)
                .and().withChoice()
                    .source(TestStates.T6_CHOICE)
                    .first(TestStates.FAULT,TestGuards.T6_CHOICE_GUARD)
                    .last(TestStates.T7_FORK)

                .and().withFork()
                    .source(TestStates.T7_FORK)
                    .target(TestStates.T7_TASK)
                .and().withExternal()
                    .source(TestStates.T7_TASK1_START)
                    .target(TestStates.T7_TASK1_STEP1).event(TestEvents.T7_TASK1_START_STEP1)
                .and().withExternal()
                    .source(TestStates.T7_TASK1_STEP1)
                    .target(TestStates.T7_TASK1_STEP2).event(TestEvents.T7_TASK1_STEP1_STEP2)
                .and().withExternal()
                    .source(TestStates.T7_TASK1_STEP2)
                    .target(TestStates.T7_TASK1_STEP3).event(TestEvents.T7_TASK1_STEP2_STEP3)
                .and().withExternal()
                    .source(TestStates.T7_TASK1_STEP3)
                    .target(TestStates.T7_TASK1_END).event(TestEvents.T7_TASK1_STEP3_END)
                .and().withJoin()
                    .source(TestStates.T7_TASK)
                    .target(TestStates.T7_JOIN)
                .and().withExternal()
                    .source(TestStates.T7_JOIN)
                    .target(TestStates.T7_CHOICE).event(TestEvents.T7_JOIN_CHOICE)
                .and().withChoice()
                    .source(TestStates.T7_CHOICE)
                    .first(TestStates.FAULT,TestGuards.T7_CHOICE_GUARD)
                    .last(TestStates.T8_FORK)

                .and().withFork()
                    .source(TestStates.T8_FORK)
                    .target(TestStates.T8_TASK)
                .and().withExternal()
                    .source(TestStates.T8_TASK1_START)
                    .target(TestStates.T8_TASK1_STEP1).event(TestEvents.T8_TASK1_START_STEP1)
                .and().withExternal()
                    .source(TestStates.T8_TASK1_STEP1)
                    .target(TestStates.T8_TASK1_STEP2).event(TestEvents.T8_TASK1_STEP1_STEP2)
                .and().withExternal()
                    .source(TestStates.T8_TASK1_STEP2)
                    .target(TestStates.T8_TASK1_STEP3).event(TestEvents.T8_TASK1_STEP2_STEP3)
                .and().withExternal()
                    .source(TestStates.T8_TASK1_STEP3)
                    .target(TestStates.T8_TASK1_END).event(TestEvents.T8_TASK1_STEP3_END)
                .and().withJoin()
                    .source(TestStates.T8_TASK)
                    .target(TestStates.T8_JOIN)
                .and().withExternal()
                    .source(TestStates.T8_JOIN)
                    .target(TestStates.T8_CHOICE).event(TestEvents.T8_JOIN_CHOICE)
                .and().withChoice()
                    .source(TestStates.T8_CHOICE)
                    .last(TestStates.BUSY_END)

                .and().withExternal()
                    .source(TestStates.BUSY_END)
                    .target(TestStates.IDLE)
                ;
        return builder.build();
    }
}
